import { Component, ElementRef, Renderer2, ViewChild } from '@angular/core';
import { ApesField } from './apes-field';
import {
  backField,
  getO,
  missing,
  replaceAll,
  fillField, isDefined,
} from '../../../../utils/utils';
import { ApiService } from '@core/services/http/api.service';
import { Events } from '@core/services/events/Events';
import { ExpressionService } from '@core/services/expression/ExpressionService';
import { SubjectService } from '@core/services/events/subject.service';
import { Subject } from 'rxjs/internal/Subject';
import { catchError, debounceTime, distinctUntilChanged, map, switchMap, tap } from 'rxjs/operators';
import { of } from 'rxjs/internal/observable/of';
import { SearchService } from '@core/services/search.service';
import { ApesDialogTransferComponent } from '@shared/dialog-transfer/apes-dialog-transfer.component';
import { ApesToolTipDirective } from '@shared/apes/tooltip';
import { toNumber } from '@shared/apes/core/util/convert';

@Component({
  selector: 'formly-field-select',
  template: `
    <div apes-row>
      <div apes-col apesMd="24">
        <ng-select #ngSelectRef
                   [items]="itemsData"
                   apes-tooltip
                   [apesTooltipTitle]="getToolTipTitle(formControl.value)"
                   [hideSelected]="true"
                   [optionWidth]="width"
                   [disabled]="to.disabled"
                   [placeholder]="to.placeholder"
                   [multiple]="multiple"
                   [formlyAttributes]="field"
                   [formControl]="formControl"
                   [virtualScroll]="true"
                   [loading]="contractLoading"
                   [typeahead]="contractinput$"
                   [searchBtn]="_searchBtn"
                   [searchable]="_ShowSearch"
                   (searchBtnFn)="onSearch()"
                   (clear)="clickClear()"
                   (change)="backData()"
                   (focus)="onFocus()"
                   (close)="onFocus()"
                   [groupBy]="showGroupBy(itemsData)"
                   (scrollToEnd)="fetchMore()">
          <!-- 表头部分 -->
          <ng-template ng-label-tmp let-item="item" let-clear="clear">
            <span class="ng-value-label">{{formatter(item, getValueFormatter())}}</span>
            <span class="ng-value-icon right" (click)="clear(item)" aria-hidden="true">×</span>
          </ng-template>
          <ng-template ng-optgroup-tmp let-item="item" let-index="index">
            <ng-container>
              {{item[groupBy]}}
            </ng-container>
          </ng-template>
          <ng-template ng-option-tmp let-item="item">
            <div class="ng-option-flex">
               <span class="ng-value-label" apes-tooltip
                     [apesTooltipTitle]="getToolTipTitle(item)">{{formatter(item, getDownFormatter())}}</span>
              <span *ngIf="showRight" style="text-align: right">{{formatter(item, getDownRightFormatter())}}</span>
            </div>
          </ng-template>

        </ng-select>
      </div>
    </div>
  `,
  styles: [`
    .ng-option-flex {
      display: flex;
      line-height: 15px;
    }

    .ng-option-flex span {
      flex: 1;
    }

    .ng-dropdown-panel .ng-dropdown-panel-items .ng-optgroup .test {
      padding: 0;
    }
  `],
})
export class FormlyFieldSelect extends ApesField {
  apesConfig: any;
  searchFormId: string;
  searchSelectApi: string;
  _ShowSearch = true;
  _searchBtn = false;
  showRight = false;
  private _current = 0;
  private _size = 20;
  multiple = false;
  width = 0;
  private _pages = 0;
  private _searchText = '';
  contractLoading = false;
  itemsData: any[] = [];
  contractinput$ = new Subject<string>();
  private defaultValue: any;
  selectedItem: any;
  _domain;
  private _transfer: boolean = false;
  @ViewChild(ApesToolTipDirective, { static: true })
  private tip: ApesToolTipDirective;
  groupBy: any;
  triggerChange: boolean = false;

  constructor(protected apiService: ApiService, protected events: Events, protected expressionService: ExpressionService,
              protected elementRef: ElementRef, protected renderer: Renderer2,
              protected subject: SubjectService, protected searchService: SearchService) {
    super(apiService, events, expressionService, subject);
    this.loadContract();
  }

  ngOnInit() {
    this.apesConfig = this.extend(this.to);
    this.width = this.to.width;
    this.searchFormId = this.to.searchFormId;
    this.searchSelectApi = this.to.api;
    this._size = this.to.size ? toNumber(this.to.size) : this._size;
    this.multiple = this.to.multiple === 'true' || false;
    if (this.to['domain']) {
      this.to.domain = replaceAll(this.to.domain, '\'', '');
      this._domain = JSON.parse(this.to.domain);
    }
    if (this.to['searchFormId']) {
      this._searchBtn = true;
    }
    // 设置下拉选项
    if (this.to['items']) {
      this.itemsData = getO(this.to['items']);
      this._ShowSearch = false;
    }
    if (this.to['dialogType'] === 'transfer') {
      this._transfer = true;
      this.multiple = true;
      this._ShowSearch = false;
      this.apesConfig.init = true;
    }
    // 无法使用，会报错
    // this.field['defaultValue']
    if (this.to['defaultValue']) {
      this.defaultValue = typeof this.to['defaultValue'] === 'string' ? JSON.parse(this.to['defaultValue']) : this.to['defaultValue'];
      this.selectedItem = this.multiple ? [this.defaultValue] : this.defaultValue;
      this.model[this.key] = this.selectedItem;
      this.formControl.patchValue(this.selectedItem);
    }
    if (this.to.hasOwnProperty('downRightFormatter')) {
      this.showRight = true;
    }

    if (this.to.hasOwnProperty('events')) {
      this.triggerChange = true;
    }
  }

  ngAfterViewInit(): void {
    // 增加 init xml 选项，表示不搜索下拉， 默认查询api作为下拉选项
    if (this.apesConfig['init'] && this.apesConfig['api']) {
      this.getItemData();
      // this._ShowSearch = false;
    }
    if (this._ShowSearch) {
      this.loadContract();
    }
    if (this.formState.hasOwnProperty('subjects')) {
      this.subject.subscribe(this.formState, 'initData', data => {
        this._searchText = '';
        if (this.apesConfig['init'] && this.apesConfig['api']) {
          this.itemsData = [];
          this._current = 0;
          this._size = 20;
          this.getItemData();
        } else if (this.to['items']) {
          this.itemsData = getO(this.to['items']);
        } else {
          this.itemsData = [];
        }
      });
    }
  }

  onFocus() {
    this.tip.apesTitle = '';
    if (this.tip.tooltip.apesVisible) {
      this.tip.hide();
    } else {
      setTimeout(() => this.tip.hide(), 1500);
    }

  }

  protected extend(data: any) {
    const result = {};
    for (const key in data) {
      const value = data[key];
      result[key] = value;
    }
    return result;
  }

  backData() {
    // 清除默认值
    this.defaultValue = null;

    this.callback();
    const context = this.to.context || {};
    backField(this, context);
  }

  callback() {
    if (!this.triggerChange) return;
    let callbackBody = this.to.events[0].callback;
    this.expressionService.evaluate(callbackBody, {
      context: this.formState,
      model: this.model,
      value: this.model[this.key],
    }, true);
  }


  private loadContract() {
    this.contractinput$.pipe(
      debounceTime(500),
      distinctUntilChanged(),
      tap(() => this.contractLoading = true),
      switchMap((term) => {
        if (missing(term)) {
          this.contractLoading = false;
          return of([]);
        }
        if (this._searchText === term) return of([]);
        this._current = 0;
        this._searchText = term;
        const data = this.getApiParams();
        return this.apiService.post(this.searchSelectApi, data).pipe(
          map((res) => {
              this.itemsData = [];
              this._searchText = term;
              this.itemsData = this.itemsData.concat(res.content);
              this._current = res.page + 1;
              this._pages = res.pages;
              return res.content;
            },
          ),
          catchError(() => of([])), // empty list on error
          tap(() => this.contractLoading = false),
        );
      }),
    ).subscribe();
  }


  fetchMore() {
    this.contractLoading = true;
    // using timeout here to simulate backend API delay
    if (this._current === this._pages) {
      this.contractLoading = false;
      return;
    }
    this.getItemData();
  }

  getItemData() {
    const data = this.getApiParams();
    this.apiService.post(this.searchSelectApi, data).subscribe(item => {
      this.contractLoading = false;
      if (!item) return;
      if (item.page === 0) {
        this.itemsData = [];
      }
      this.itemsData = this.itemsData.concat(item.content);
      this._current = item.page + 1;
      this._pages = item.pages;
      // 设置默认值
      if (this.defaultValue) {
        let tempVal = this.itemsData.filter(item => item.id === this.defaultValue.id);
        if (tempVal && tempVal.length == 0) {
          this.itemsData.unshift(this.defaultValue);
          this.selectedItem = this.multiple ? [this.defaultValue] : this.defaultValue;
        } else {
          this.selectedItem = this.multiple ? tempVal : tempVal[0];
        }
      }
    });
  }

  /*
  * 弹窗事件
  * */
  onSearch() {
    const key = this.to.componentRef.field.key;
    const options = { multiple: this.multiple }; // 声明查询条件
    if (this._domain) {
      const group = fillField(this._domain, this.formState, true, this._searchText);
      if (!(group.filters.length === 0 && group.groups.length === 0)) {
        options['domain'] = group;
      }
    }
    // 弹窗api与下拉api保持一致
    if (this.searchSelectApi) options['api'] = this.searchSelectApi;
    if (this.to.apiParams) {
      options['apiParams'] = fillField(getO(this.to.apiParams), this.formState, false, this._searchText);
    }
    if (this.to.showField) {
      options['showField'] = fillField(getO(this.to.showField), this.formState, false, this._searchText);
    }
    if (this.to.uniqueKey) {
      options['uniqueKey'] = this.to.uniqueKey;
      options['addSame'] = false; //this.to.addSame == false ? this.to.addSame : true;
    }
    if (this.to.hasOwnProperty('selectLastStage')) {
      options['selectLastStage'] = this.to.selectLastStage;
    }
    options['items'] = [...this.itemsData];
    options['valueFormatter'] = this.getValueFormatter();
    options['value'] = this.formControl.value;
    this.searchService.openDialog(this.searchFormId, options, (result: any) => {
      if (result && result.length > 0) {
        let id = options['uniqueKey'] ? options['uniqueKey'] : 'id';
        this.itemsData = this.getUniqueArray([...this.itemsData, ...result], id);
        if (this.multiple) {
          if (!this._transfer && this.formControl.value !== null && this.formControl.value !== undefined) {
            result = this.getUniqueArray([...this.formControl.value, ...result], id);
          }
          this.form.get(key).patchValue(result);
        } else {
          this.form.get(key).patchValue(result[0]);
          this.backData();
        }
      } else if (result && result instanceof Object) {
        this.form.get(key).patchValue(result);
      }
    }, this._transfer ? ApesDialogTransferComponent : null).subscribe();
  }


  // 获取不重复项数组
  private getUniqueArray(array: any[], id: string): any[] {
    array = Array.from(new Set(array));
    if (typeof array[0] === 'object') {
      const res = new Map();
      array = array.filter((a) => !res.has(a[id]) && res.set(a[id], 1));
    }
    return array;
  }

  getValueFormatter(): string {
    return this.to.valueFormatter || 'value.name';
  }

  getDownFormatter(): string {
    return this.to.downFormatter || '\'[ \' + value.id + \' ]\' + \' \' + value.name';
  }

  getDownRightFormatter() {
    return this.to.downRightFormatter || '\'[ \' + value.id + \' ]\' + \' \' + value.name';
  }

  getApiParams() {
    const data = { key: this._searchText, page: this._current, size: this._size };
    if (this._domain) {
      const group = fillField(this._domain, this.formState, false, this._searchText);
      data['group'] = group;
    }
    return data;
  }

  getToolTipTitle(value) {
    if (isDefined(value)) {
      if (value instanceof Array) {
        let strTip = '';
        value.forEach(item => {
          let v = this.formatter(item, this.getValueFormatter());
          strTip == '' ? strTip = v : strTip = strTip + ',' + v;
        });
        return strTip;
      }
      return this.formatter(value, this.getValueFormatter());
    } else {
      return '';
    }

  }

  formatter(value: any, expression: string) {
    return this.expressionService.evaluate(expression, {
      context: this.formState,
      model: this.formState.model,
      value: value,
    });
  }

  showGroupBy(items) {
    let check = false;
    let groupBy = '';
    if (items != undefined && items.length != 0) {
      items.forEach((item) => {
        if (item && item.hasOwnProperty('groupBy')) {
          check = true;
          groupBy = item.groupBy;
        }
      });
    }
    if (check) {
      this.groupBy = groupBy;
      return groupBy;
    } else {
      return false;
    }
  }

  clickClear() {
    setTimeout(() => {
      if (this.formState.hasOwnProperty('subjects')) {
        this.subject.publish(this.formState, 'initData', true);
      }
    }, 100);
  }
}
